#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "i2c_custom.h"
#include "pcf8563.h"

const static char *TAG = "pcf8563.c";

static SemaphoreHandle_t xPcfSemaphore; /* pcf mutex */
static struct tm timeinfo;              /* real time */

/**
 * @brief bcd_to_dec
 *
 * @param val
 * @return uint8_t
 */
static uint8_t bcd_to_dec(uint8_t val)
{
    return (val >> 4) * 10 + (val & 0x0f);
}

/**
 * @brief dec_to_bcd
 *
 * @param val
 * @return uint8_t
 */
static uint8_t dec_to_bcd(uint8_t val)
{
    return ((val / 10) << 4) + (val % 10);
}

/**
 * @brief pcf8563_read_reg
 *
 * @param i2c_num
 * @param device_address
 * @param write_buffer
 * @param write_size
 * @param read_buffer
 * @param read_size
 * @return esp_err_t
 */
static esp_err_t pcf8563_read_reg(i2c_port_t i2c_num, uint8_t device_address, const uint8_t write_buffer, size_t write_size, uint8_t *read_buffer, size_t read_size)
{
    return i2c_master_write_read_device(i2c_num, device_address, &write_buffer, write_size, read_buffer, read_size, portTICK_PERIOD_MS);
}

/**
 * @brief pcf8563_get_time
 *
 * @param rtc_time
 * @return esp_err_t
 */
static esp_err_t pcf8563_get_time(struct tm *rtc_time)
{
    /* Time and date registers */
    uint8_t _time_buffer[7];
    pcf8563_read_reg(I2C_MASTER_NUM, PCF8563_ADDR, PCF8563_ADDR_TIME, 1, _time_buffer, 7);

    /* convert to unix time structure */
    rtc_time->tm_sec = bcd_to_dec(_time_buffer[0] & 0x7F);     /* sec */
    rtc_time->tm_min = bcd_to_dec(_time_buffer[1] & 0x7F);     /* min */
    rtc_time->tm_hour = bcd_to_dec(_time_buffer[2] & 0x3F);    /* hour */
    rtc_time->tm_mday = bcd_to_dec(_time_buffer[3] & 0x3F);    /* m_day */
    rtc_time->tm_wday = _time_buffer[4] & 0x07;                /* w_day */
    rtc_time->tm_mon = bcd_to_dec(_time_buffer[5] & 0x1F) - 1; /* mon */
    rtc_time->tm_year = bcd_to_dec(_time_buffer[6]) + 2000;    /* year */
    rtc_time->tm_isdst = 0;

#if PCF8563_DEBUG
    ESP_LOGW(TAG, "%02d:%02d:%02d %d-%d-%d 星期%d",
             rtc_time->tm_hour /*hour*/,
             rtc_time->tm_min /*min*/,
             rtc_time->tm_sec /*sec*/,
             rtc_time->tm_year /*year*/,
             rtc_time->tm_mon /*month*/,
             rtc_time->tm_mday /*m_day*/,
             rtc_time->tm_wday + 1 /*w_day*/);
#endif

    return ESP_OK;
}

/**
 * @brief calculateWeekday
 *
 * @param rtc_time
 * @return esp_err_t
 */
uint16_t pcf8563_calculate_Weekday(uint16_t year, uint8_t month, uint8_t day)
{
    if (month < 3)
    {
        month += 12;
        year--;
    }
    int J = year / 100;
    int K = year % 100;
    int h = (day + 13 * (month + 1) / 5 + K + K / 4 + J / 4 - 2 * J) % 7;

    if (h == 0)
    {
        return 6;
    }
    else
    {
        return h - 1;
    }
}

/**
 * @brief pcf8563_i2c_master_init
 *
 * @param port
 * @param sda
 * @param scl
 * @return esp_err_t
 */
esp_err_t pcf8563_i2c_master_init(i2c_port_t port, int sda, int scl)
{
    i2c_config_t i2c_config = i2c_conf_func();
    // i2c_param_config(I2C_NUM_0, &i2c_config);
    // i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0);
    i2c_param_config(port, &i2c_config);
    return i2c_driver_install(port, I2C_MODE_MASTER, 0, 0, 0);
}

/**
 * @brief pcf8563_set_time
 *
 * @param sec
 * @param min
 * @param hour
 * @param mday
 * @param wday
 * @param mon
 * @param year
 * @return esp_err_t
 */
esp_err_t pcf8563_set_time(uint8_t sec, uint8_t min, uint8_t hour, uint8_t mday, uint8_t wday, uint8_t mon, uint16_t year)
{
    /* check set time */
    if (sec >= 60 || min >= 60 || hour >= 24 || mday > 32 || wday > 6 || mon > 12 || year < 1900 || year >= 2100)
    {
#if PCF8563_DEBUG
        ESP_LOGE(TAG, "set time format is error.");
#endif
        return ESP_FAIL;
    }

    /* Time and date registers */
    uint8_t _set_time_buffer[8];

    _set_time_buffer[0] = PCF8563_ADDR_TIME;
    _set_time_buffer[1] = dec_to_bcd(sec);
    _set_time_buffer[2] = dec_to_bcd(min);
    _set_time_buffer[3] = dec_to_bcd(hour);
    _set_time_buffer[4] = dec_to_bcd(mday);
    _set_time_buffer[5] = wday;            // tm_wday is 0 to 6
    _set_time_buffer[6] = dec_to_bcd(mon); // tm_mon is 1 to 12
    _set_time_buffer[7] = dec_to_bcd(year - 2000);

    return i2c_master_write_to_device(I2C_MASTER_NUM, PCF8563_ADDR, _set_time_buffer, 8, portMAX_DELAY);
}

/**
 * @brief pcf8563_get_data
 *
 * @param data_type
 * @return uint16_t
 */
uint16_t pcf8563_get_data(pcf8563_get_data_type_t data_type)
{
    switch (data_type)
    {
    case SEC:
        return timeinfo.tm_sec;
    case MIN:
        return timeinfo.tm_min;
    case HOUR:
        return timeinfo.tm_hour;
    case MDAY:
        return timeinfo.tm_mday;
    case MON:
        return timeinfo.tm_mon;
    case YEAR:
        return timeinfo.tm_year;
    case WDAY:
        return timeinfo.tm_wday;
    default:
        return ESP_OK;
    }
}

void pcf8563_task(void *arg)
{
    // ESP_ERROR_CHECK(pcf8563_i2c_master_init(I2C_MASTER_NUM, I2C_MASTER_SDA_IO, I2C_MASTER_SCL_IO));
    // ESP_ERROR_CHECK(pcf_set_time(0, 0, 0, 29, 0, 0, 2024));

    /* pcf mutex create start */
    xPcfSemaphore = xSemaphoreCreateMutex(); /* create aht20 mutex */
    if (xPcfSemaphore != NULL)
    {
        ESP_LOGI(TAG, "Creat xPcfSemaphore successfully");
    }
    else
    {
        /* error handler begin */
        ESP_LOGE(TAG, "Creat xPcfSemaphore error. err msg->%s,,%s", __FILE__, __func__);
        vSemaphoreDelete(xPcfSemaphore);
        /* error handler end */
    }
    /* pcf mutex create end */

    while (1)
    {
        if (pdTRUE == xSemaphoreTake(xPcfSemaphore, portMAX_DELAY))
        {
            pcf8563_get_time(&timeinfo);   /* get time */
            xSemaphoreGive(xPcfSemaphore); /* release pcf mutex */
        }
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}